#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "opus.h"
#include <stdio.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>


// HELPER MARCOs for wav header
#define WRITE_U32(buf, x) *(buf)     = (unsigned char)((x)&0xff);\
                          *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\
                          *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\
                          *((buf)+3) = (unsigned char)(((x)>>24)&0xff);

#define WRITE_U16(buf, x) *(buf)     = (unsigned char)((x)&0xff);\
                          *((buf)+1) = (unsigned char)(((x)>>8)&0xff);

#define READ_U16(buf, offset)      ((buf[offset]) | (unsigned short)buf[offset + 1] << 8)

static int write_wav_header(FILE * out, int channels, int samplerate){

	int bits = 16;
	unsigned char headbuf[44];  /* The whole buffer */

	int knownlength = 0;

	int bytespersec = channels * samplerate * bits / 8;
	int align = channels * bits / 8;
	int samplesize = bits;


	/*
	here's a good ref...
	http://www.lightlink.com/tjweber/StripWav/Canon.html
	Based on the link above,
	Actually, we are writting a simplified version of Wav.
	*/
	memcpy(headbuf, "RIFF", 4);
	WRITE_U32(headbuf + 4, 0); // fileLength - 8 (4 bytes)
	memcpy(headbuf + 8, "WAVE", 4);
	memcpy(headbuf + 12, "fmt ", 4);
	WRITE_U32(headbuf + 16, 16); // length of fmt data
	WRITE_U16(headbuf + 20, 1);  /* format , 1==PCM */
	WRITE_U16(headbuf + 22, channels); // 1==miss mono, 2== stereo 
	WRITE_U32(headbuf + 24, samplerate);
	WRITE_U32(headbuf + 28, bytespersec);
	WRITE_U16(headbuf + 32, align);
	WRITE_U16(headbuf + 34, samplesize);  // 16 or 8 
	memcpy(headbuf + 36, "data", 4);
	WRITE_U32(headbuf + 40, 0); // length of data block  fileLength - 44

	if (fwrite(headbuf, 1, 44, out) != 44)
	{
		return -1;
	}

	return 0;
}

//https://cdn-gomark.17zuoye.cn/81bf91/training/opus/20210226/81bf91de780d11eb818402420a082354
struct MemoryStruct {
  char *memory;
  size_t size;
};
 
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
 
  char *ptr = realloc(mem->memory, mem->size + realsize + 1);
  if(ptr == NULL) {
    /* out of memory! */ 
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }
 
  mem->memory = ptr;
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;
  return realsize;
}
 
int main(int argc, char *argv[])
{
    CURL *curl_handle;             //定义CURL类型的指针
    CURLcode res;           //定义CURLcode类型的变量，保存返回状态码
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);  /* will be grown as needed by the realloc above */ 
    chunk.size = 0;    /* no data at this point */ 
 
    if(argc!=2)
    {
        printf("Usage : file <url>;/n");
        exit(1);
    }

    curl_handle = curl_easy_init();        //初始化一个CURL类型的指针
    if(curl_handle != NULL)
    {
        curl_easy_setopt(curl_handle, CURLOPT_URL, argv[1]);       
        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
        res = curl_easy_perform(curl_handle);
        if(res != CURLE_OK) {
          fprintf(stderr, "curl_easy_perform() failed: %s\n",
          curl_easy_strerror(res));
          return -1;
        }
        else {
              //printf("%s\n", chunk.memory);
              printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
        }
        curl_easy_cleanup(curl_handle);
    }

   OpusDecoder *decoder; 
   int err;
   int sample_rate = 16000;
   int channels = 1;
   decoder = opus_decoder_create(sample_rate, channels, &err);;
   int processed = 0;
   
   FILE * wav = fopen("out.wav", "wb");
   write_wav_header(wav, channels, sample_rate);
   while (processed <= chunk.size) {
     //frame len
     unsigned short temp = READ_U16(chunk.memory, processed);
     printf("frame len %d \n", temp);
     processed += 2;
     //decode
     int frame_size = 2048;
     void *decoded = malloc(frame_size * channels * sizeof(opus_int16));
     int samples =  opus_decode(decoder, chunk.memory + processed, temp, decoded, frame_size, 0);
     fwrite(decoded, samples * channels * sizeof(opus_int16), 1, wav);
     processed += temp;
   }
   fclose(wav);
}
